home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / python2.4 / idlelib / configDialog.py < prev    next >
Text File  |  2005-10-18  |  57KB  |  1,209 lines

  1. """IDLE Configuration Dialog: support user customization of IDLE by GUI
  2.  
  3. Customize font faces, sizes, and colorization attributes.  Set indentation
  4. defaults.  Customize keybindings.  Colorization and keybindings can be
  5. saved as user defined sets.  Select startup options including shell/editor
  6. and default window size.  Define additional help sources.
  7.  
  8. Note that tab width in IDLE is currently fixed at eight due to Tk issues.
  9. Refer to comment in EditorWindow autoindent code for details.
  10.  
  11. """
  12. from Tkinter import *
  13. import tkMessageBox, tkColorChooser, tkFont
  14. import string, copy
  15.  
  16. from configHandler import idleConf
  17. from dynOptionMenuWidget import DynOptionMenu
  18. from tabpage import TabPageSet
  19. from keybindingDialog import GetKeysDialog
  20. from configSectionNameDialog import GetCfgSectionNameDialog
  21. from configHelpSourceEdit import GetHelpSourceDialog
  22.  
  23. class ConfigDialog(Toplevel):
  24.     """
  25.     configuration dialog for idle
  26.     """
  27.     def __init__(self,parent,title):
  28.         Toplevel.__init__(self, parent)
  29.         self.configure(borderwidth=5)
  30.         self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
  31.                 parent.winfo_rooty()+30))
  32.         #Theme Elements. Each theme element key is its display name.
  33.         #The first value of the tuple is the sample area tag name.
  34.         #The second value is the display name list sort index.
  35.         self.themeElements={'Normal Text':('normal','00'),
  36.             'Python Keywords':('keyword','01'),
  37.             'Python Definitions':('definition','02'),
  38.             'Python Builtins':('builtin', '03'),
  39.             'Python Comments':('comment','04'),
  40.             'Python Strings':('string','05'),
  41.             'Selected Text':('hilite','06'),
  42.             'Found Text':('hit','07'),
  43.             'Cursor':('cursor','08'),
  44.             'Error Text':('error','09'),
  45.             'Shell Normal Text':('console','10'),
  46.             'Shell Stdout Text':('stdout','11'),
  47.             'Shell Stderr Text':('stderr','12'),
  48.             }
  49.         self.ResetChangedItems() #load initial values in changed items dict
  50.         self.CreateWidgets()
  51.         self.resizable(height=FALSE,width=FALSE)
  52.         self.transient(parent)
  53.         self.grab_set()
  54.         self.protocol("WM_DELETE_WINDOW", self.Cancel)
  55.         self.parent = parent
  56.         self.tabPages.focus_set()
  57.         #key bindings for this dialog
  58.         #self.bind('<Escape>',self.Cancel) #dismiss dialog, no save
  59.         #self.bind('<Alt-a>',self.Apply) #apply changes, save
  60.         #self.bind('<F1>',self.Help) #context help
  61.         self.LoadConfigs()
  62.         self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
  63.         self.wait_window()
  64.  
  65.     def CreateWidgets(self):
  66.         self.tabPages = TabPageSet(self,
  67.                 pageNames=['Fonts/Tabs','Highlighting','Keys','General'])
  68.         self.tabPages.ChangePage()#activates default (first) page
  69.         frameActionButtons = Frame(self)
  70.         #action buttons
  71.         self.buttonHelp = Button(frameActionButtons,text='Help',
  72.                 command=self.Help,takefocus=FALSE)
  73.         self.buttonOk = Button(frameActionButtons,text='Ok',
  74.                 command=self.Ok,takefocus=FALSE)
  75.         self.buttonApply = Button(frameActionButtons,text='Apply',
  76.                 command=self.Apply,takefocus=FALSE)
  77.         self.buttonCancel = Button(frameActionButtons,text='Cancel',
  78.                 command=self.Cancel,takefocus=FALSE)
  79.         self.CreatePageFontTab()
  80.         self.CreatePageHighlight()
  81.         self.CreatePageKeys()
  82.         self.CreatePageGeneral()
  83.         self.buttonHelp.pack(side=RIGHT,padx=5,pady=5)
  84.         self.buttonOk.pack(side=LEFT,padx=5,pady=5)
  85.         self.buttonApply.pack(side=LEFT,padx=5,pady=5)
  86.         self.buttonCancel.pack(side=LEFT,padx=5,pady=5)
  87.         frameActionButtons.pack(side=BOTTOM)
  88.         self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
  89.  
  90.     def CreatePageFontTab(self):
  91.         #tkVars
  92.         self.fontSize=StringVar(self)
  93.         self.fontBold=BooleanVar(self)
  94.         self.fontName=StringVar(self)
  95.         self.spaceNum=IntVar(self)
  96.         #self.tabCols=IntVar(self)
  97.         self.indentBySpaces=BooleanVar(self)
  98.         self.editFont=tkFont.Font(self,('courier',10,'normal'))
  99.         ##widget creation
  100.         #body frame
  101.         frame=self.tabPages.pages['Fonts/Tabs']['page']
  102.         #body section frames
  103.         frameFont=Frame(frame,borderwidth=2,relief=GROOVE)
  104.         frameIndent=Frame(frame,borderwidth=2,relief=GROOVE)
  105.         #frameFont
  106.         labelFontTitle=Label(frameFont,text='Set Base Editor Font')
  107.         frameFontName=Frame(frameFont)
  108.         frameFontParam=Frame(frameFont)
  109.         labelFontNameTitle=Label(frameFontName,justify=LEFT,
  110.                 text='Font :')
  111.         self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
  112.                 exportselection=FALSE)
  113.         self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
  114.         scrollFont=Scrollbar(frameFontName)
  115.         scrollFont.config(command=self.listFontName.yview)
  116.         self.listFontName.config(yscrollcommand=scrollFont.set)
  117.         labelFontSizeTitle=Label(frameFontParam,text='Size :')
  118.         self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None,
  119.             command=self.SetFontSample)
  120.         checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold,
  121.             onvalue=1,offvalue=0,text='Bold',command=self.SetFontSample)
  122.         frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
  123.         self.labelFontSample=Label(frameFontSample,
  124.                 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
  125.                 justify=LEFT,font=self.editFont)
  126.         #frameIndent
  127.         labelIndentTitle=Label(frameIndent,text='Set Indentation Defaults')
  128.         frameIndentType=Frame(frameIndent)
  129.         frameIndentSize=Frame(frameIndent)
  130.         labelIndentTypeTitle=Label(frameIndentType,
  131.                 text='Choose indentation type :')
  132.         radioUseSpaces=Radiobutton(frameIndentType,variable=self.indentBySpaces,
  133.             value=1,text='Tab key inserts spaces')
  134.         radioUseTabs=Radiobutton(frameIndentType,variable=self.indentBySpaces,
  135.             value=0,text='Tab key inserts tabs')
  136.         labelIndentSizeTitle=Label(frameIndentSize,
  137.                 text='Choose indentation size :')
  138.         labelSpaceNumTitle=Label(frameIndentSize,justify=LEFT,
  139.                 text='indent width')
  140.         self.scaleSpaceNum=Scale(frameIndentSize,variable=self.spaceNum,
  141.                 orient='horizontal',tickinterval=2,from_=2,to=16)
  142.         #labeltabColsTitle=Label(frameIndentSize,justify=LEFT,
  143.         #        text='when tab key inserts tabs,\ncolumns per tab')
  144.         #self.scaleTabCols=Scale(frameIndentSize,variable=self.tabCols,
  145.         #        orient='horizontal',tickinterval=2,from_=2,to=8)
  146.         #widget packing
  147.         #body
  148.         frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
  149.         frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y)
  150.         #frameFont
  151.         labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  152.         frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
  153.         frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
  154.         labelFontNameTitle.pack(side=TOP,anchor=W)
  155.         self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
  156.         scrollFont.pack(side=LEFT,fill=Y)
  157.         labelFontSizeTitle.pack(side=LEFT,anchor=W)
  158.         self.optMenuFontSize.pack(side=LEFT,anchor=W)
  159.         checkFontBold.pack(side=LEFT,anchor=W,padx=20)
  160.         frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  161.         self.labelFontSample.pack(expand=TRUE,fill=BOTH)
  162.         #frameIndent
  163.         labelIndentTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  164.         frameIndentType.pack(side=TOP,padx=5,fill=X)
  165.         frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH)
  166.         labelIndentTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  167.         radioUseSpaces.pack(side=TOP,anchor=W,padx=5)
  168.         radioUseTabs.pack(side=TOP,anchor=W,padx=5)
  169.         labelIndentSizeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  170.         labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
  171.         self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
  172.         #labeltabColsTitle.pack(side=TOP,anchor=W,padx=5)
  173.         #self.scaleTabCols.pack(side=TOP,padx=5,fill=X)
  174.         return frame
  175.  
  176.     def CreatePageHighlight(self):
  177.         self.builtinTheme=StringVar(self)
  178.         self.customTheme=StringVar(self)
  179.         self.fgHilite=BooleanVar(self)
  180.         self.colour=StringVar(self)
  181.         self.fontName=StringVar(self)
  182.         self.themeIsBuiltin=BooleanVar(self)
  183.         self.highlightTarget=StringVar(self)
  184.         ##widget creation
  185.         #body frame
  186.         frame=self.tabPages.pages['Highlighting']['page']
  187.         #body section frames
  188.         frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
  189.         frameTheme=Frame(frame,borderwidth=2,relief=GROOVE)
  190.         #frameCustom
  191.         self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
  192.             font=('courier',12,''),cursor='hand2',width=21,height=10,
  193.             takefocus=FALSE,highlightthickness=0,wrap=NONE)
  194.         text=self.textHighlightSample
  195.         text.bind('<Double-Button-1>',lambda e: 'break')
  196.         text.bind('<B1-Motion>',lambda e: 'break')
  197.         textAndTags=(('#you can click here','comment'),('\n','normal'),
  198.             ('#to choose items','comment'),('\n','normal'),('def','keyword'),
  199.             (' ','normal'),('func','definition'),('(param):','normal'),
  200.             ('\n  ','normal'),('"""string"""','string'),('\n  var0 = ','normal'),
  201.             ("'string'",'string'),('\n  var1 = ','normal'),("'selected'",'hilite'),
  202.             ('\n  var2 = ','normal'),("'found'",'hit'),
  203.             ('\n  var3 = ','normal'),('list', 'builtin'), ('(','normal'),
  204.             ('None', 'builtin'),(')\n\n','normal'),
  205.             (' error ','error'),(' ','normal'),('cursor |','cursor'),
  206.             ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
  207.             (' ','normal'),('stderr','stderr'),('\n','normal'))
  208.         for txTa in textAndTags:
  209.             text.insert(END,txTa[0],txTa[1])
  210.         for element in self.themeElements.keys():
  211.             text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
  212.                 lambda event,elem=element: event.widget.winfo_toplevel()
  213.                 .highlightTarget.set(elem))
  214.         text.config(state=DISABLED)
  215.         self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
  216.         frameFgBg=Frame(frameCustom)
  217.         labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting')
  218.         buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
  219.             command=self.GetColour,highlightthickness=0)
  220.         self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
  221.             self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
  222.         self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
  223.             value=1,text='Foreground',command=self.SetColourSampleBinding)
  224.         self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
  225.             value=0,text='Background',command=self.SetColourSampleBinding)
  226.         self.fgHilite.set(1)
  227.         buttonSaveCustomTheme=Button(frameCustom,
  228.             text='Save as New Custom Theme',command=self.SaveAsNewTheme)
  229.         #frameTheme
  230.         labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme')
  231.         labelTypeTitle=Label(frameTheme,text='Select : ')
  232.         self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
  233.             value=1,command=self.SetThemeType,text='a Built-in Theme')
  234.         self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
  235.             value=0,command=self.SetThemeType,text='a Custom Theme')
  236.         self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
  237.             self.builtinTheme,None,command=None)
  238.         self.optMenuThemeCustom=DynOptionMenu(frameTheme,
  239.             self.customTheme,None,command=None)
  240.         self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
  241.                 command=self.DeleteCustomTheme)
  242.         ##widget packing
  243.         #body
  244.         frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
  245.         frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y)
  246.         #frameCustom
  247.         labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  248.         self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
  249.         frameFgBg.pack(side=TOP,padx=5,pady=0)
  250.         self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
  251.             fill=BOTH)
  252.         buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
  253.         self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
  254.         self.radioFg.pack(side=LEFT,anchor=E)
  255.         self.radioBg.pack(side=RIGHT,anchor=W)
  256.         buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  257.         #frameTheme
  258.         labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  259.         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  260.         self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
  261.         self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
  262.         self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
  263.         self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
  264.         self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
  265.         return frame
  266.  
  267.     def CreatePageKeys(self):
  268.         #tkVars
  269.         self.bindingTarget=StringVar(self)
  270.         self.builtinKeys=StringVar(self)
  271.         self.customKeys=StringVar(self)
  272.         self.keysAreBuiltin=BooleanVar(self)
  273.         self.keyBinding=StringVar(self)
  274.         ##widget creation
  275.         #body frame
  276.         frame=self.tabPages.pages['Keys']['page']
  277.         #body section frames
  278.         frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
  279.         frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE)
  280.         #frameCustom
  281.         frameTarget=Frame(frameCustom)
  282.         labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings')
  283.         labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
  284.         scrollTargetY=Scrollbar(frameTarget)
  285.         scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
  286.         self.listBindings=Listbox(frameTarget,takefocus=FALSE,
  287.                 exportselection=FALSE)
  288.         self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
  289.         scrollTargetY.config(command=self.listBindings.yview)
  290.         scrollTargetX.config(command=self.listBindings.xview)
  291.         self.listBindings.config(yscrollcommand=scrollTargetY.set)
  292.         self.listBindings.config(xscrollcommand=scrollTargetX.set)
  293.         self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
  294.             command=self.GetNewKeys,state=DISABLED)
  295.         buttonSaveCustomKeys=Button(frameCustom,
  296.                 text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
  297.         #frameKeySets
  298.         labelKeysTitle=Label(frameKeySets,text='Select a Key Set')
  299.         labelTypeTitle=Label(frameKeySets,text='Select : ')
  300.         self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
  301.             value=1,command=self.SetKeysType,text='a Built-in Key Set')
  302.         self.radioKeysCustom=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
  303.             value=0,command=self.SetKeysType,text='a Custom Key Set')
  304.         self.optMenuKeysBuiltin=DynOptionMenu(frameKeySets,
  305.             self.builtinKeys,None,command=None)
  306.         self.optMenuKeysCustom=DynOptionMenu(frameKeySets,
  307.             self.customKeys,None,command=None)
  308.         self.buttonDeleteCustomKeys=Button(frameKeySets,text='Delete Custom Key Set',
  309.                 command=self.DeleteCustomKeys)
  310.         ##widget packing
  311.         #body
  312.         frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
  313.         frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y)
  314.         #frameCustom
  315.         labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  316.         buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  317.         self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  318.         frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
  319.         #frame target
  320.         frameTarget.columnconfigure(0,weight=1)
  321.         frameTarget.rowconfigure(1,weight=1)
  322.         labelTargetTitle.grid(row=0,column=0,columnspan=2,sticky=W)
  323.         self.listBindings.grid(row=1,column=0,sticky=NSEW)
  324.         scrollTargetY.grid(row=1,column=1,sticky=NS)
  325.         scrollTargetX.grid(row=2,column=0,sticky=EW)
  326.         #frameKeySets
  327.         labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  328.         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  329.         self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5)
  330.         self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
  331.         self.optMenuKeysBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
  332.         self.optMenuKeysCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
  333.         self.buttonDeleteCustomKeys.pack(side=TOP,fill=X,padx=5,pady=5)
  334.         return frame
  335.  
  336.     def CreatePageGeneral(self):
  337.         #tkVars
  338.         self.winWidth=StringVar(self)
  339.         self.winHeight=StringVar(self)
  340.         self.paraWidth=StringVar(self)
  341.         self.startupEdit=IntVar(self)
  342.         self.autoSave=IntVar(self)
  343.         self.encoding=StringVar(self)
  344.         self.userHelpBrowser=BooleanVar(self)
  345.         self.helpBrowser=StringVar(self)
  346.         #widget creation
  347.         #body
  348.         frame=self.tabPages.pages['General']['page']
  349.         #body section frames
  350.         frameRun=Frame(frame,borderwidth=2,relief=GROOVE)
  351.         frameSave=Frame(frame,borderwidth=2,relief=GROOVE)
  352.         frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
  353.         frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE)
  354.         frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE)
  355.         frameHelp=Frame(frame,borderwidth=2,relief=GROOVE)
  356.         #frameRun
  357.         labelRunTitle=Label(frameRun,text='Startup Preferences')
  358.         labelRunChoiceTitle=Label(frameRun,text='At Startup')
  359.         radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
  360.             value=1,command=self.SetKeysType,text="Open Edit Window")
  361.         radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
  362.             value=0,command=self.SetKeysType,text='Open Shell Window')
  363.         #frameSave
  364.         labelSaveTitle=Label(frameSave,text='Autosave Preference')
  365.         labelRunSaveTitle=Label(frameSave,text='At Start of Run (F5)  ')
  366.         radioSaveAsk=Radiobutton(frameSave,variable=self.autoSave,
  367.             value=0,command=self.SetKeysType,text="Prompt to Save")
  368.         radioSaveAuto=Radiobutton(frameSave,variable=self.autoSave,
  369.             value=1,command=self.SetKeysType,text='No Prompt')
  370.         #frameWinSize
  371.         labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+
  372.                 '  (in characters)')
  373.         labelWinWidthTitle=Label(frameWinSize,text='Width')
  374.         entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth,
  375.                 width=3)
  376.         labelWinHeightTitle=Label(frameWinSize,text='Height')
  377.         entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight,
  378.                 width=3)
  379.         #paragraphFormatWidth
  380.         labelParaWidthTitle=Label(frameParaSize,text='Paragraph reformat'+
  381.                 ' width (in characters)')
  382.         entryParaWidth=Entry(frameParaSize,textvariable=self.paraWidth,
  383.                 width=3)
  384.         #frameEncoding
  385.         labelEncodingTitle=Label(frameEncoding,text="Default Source Encoding")
  386.         radioEncLocale=Radiobutton(frameEncoding,variable=self.encoding,
  387.             value="locale",text="Locale-defined")
  388.         radioEncUTF8=Radiobutton(frameEncoding,variable=self.encoding,
  389.             value="utf-8",text="UTF-8")
  390.         radioEncNone=Radiobutton(frameEncoding,variable=self.encoding,
  391.             value="none",text="None")
  392.         #frameHelp
  393.         ##labelHelpTitle=Label(frameHelp,text='Help Options')
  394.         frameHelpList=Frame(frameHelp)
  395.         frameHelpListButtons=Frame(frameHelpList)
  396.         labelHelpListTitle=Label(frameHelpList,text='Additional Help Sources:')
  397.         scrollHelpList=Scrollbar(frameHelpList)
  398.         self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
  399.                 exportselection=FALSE)
  400.         scrollHelpList.config(command=self.listHelp.yview)
  401.         self.listHelp.config(yscrollcommand=scrollHelpList.set)
  402.         self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
  403.         self.buttonHelpListEdit=Button(frameHelpListButtons,text='Edit',
  404.                 state=DISABLED,width=8,command=self.HelpListItemEdit)
  405.         self.buttonHelpListAdd=Button(frameHelpListButtons,text='Add',
  406.                 width=8,command=self.HelpListItemAdd)
  407.         self.buttonHelpListRemove=Button(frameHelpListButtons,text='Remove',
  408.                 state=DISABLED,width=8,command=self.HelpListItemRemove)
  409.         # the following is better handled by the BROWSER environment
  410.         # variable under unix/linux
  411.         #checkHelpBrowser=Checkbutton(frameHelp,variable=self.userHelpBrowser,
  412.         #    onvalue=1,offvalue=0,text='user specified (html) help browser:',
  413.         #    command=self.OnCheckUserHelpBrowser)
  414.         #self.entryHelpBrowser=Entry(frameHelp,textvariable=self.helpBrowser,
  415.         #        width=40)
  416.         #widget packing
  417.         #body
  418.         frameRun.pack(side=TOP,padx=5,pady=5,fill=X)
  419.         frameSave.pack(side=TOP,padx=5,pady=5,fill=X)
  420.         frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
  421.         frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X)
  422.         frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X)
  423.         frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  424.         #frameRun
  425.         labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  426.         labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  427.         radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  428.         radioStartupEdit.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  429.         #frameSave
  430.         labelSaveTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  431.         labelRunSaveTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  432.         radioSaveAuto.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  433.         radioSaveAsk.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  434.         #frameWinSize
  435.         labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  436.         entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  437.         labelWinHeightTitle.pack(side=RIGHT,anchor=E,pady=5)
  438.         entryWinWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  439.         labelWinWidthTitle.pack(side=RIGHT,anchor=E,pady=5)
  440.         #paragraphFormatWidth
  441.         labelParaWidthTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  442.         entryParaWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  443.         #frameEncoding
  444.         labelEncodingTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  445.         radioEncNone.pack(side=RIGHT,anchor=E,pady=5)
  446.         radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5)
  447.         radioEncLocale.pack(side=RIGHT,anchor=E,pady=5)
  448.         #frameHelp
  449.         ##labelHelpTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  450.         frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
  451.         frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  452.         labelHelpListTitle.pack(side=TOP,anchor=W)
  453.         scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
  454.         self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
  455.         self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
  456.         self.buttonHelpListAdd.pack(side=TOP,anchor=W)
  457.         self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
  458.         #checkHelpBrowser.pack(side=TOP,anchor=W,padx=5)
  459.         #self.entryHelpBrowser.pack(side=TOP,anchor=W,padx=5,pady=5)
  460.         return frame
  461.  
  462.     def AttachVarCallbacks(self):
  463.         self.fontSize.trace_variable('w',self.VarChanged_fontSize)
  464.         self.fontName.trace_variable('w',self.VarChanged_fontName)
  465.         self.fontBold.trace_variable('w',self.VarChanged_fontBold)
  466.         self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
  467.         #self.tabCols.trace_variable('w',self.VarChanged_tabCols)
  468.         self.indentBySpaces.trace_variable('w',self.VarChanged_indentBySpaces)
  469.         self.colour.trace_variable('w',self.VarChanged_colour)
  470.         self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
  471.         self.customTheme.trace_variable('w',self.VarChanged_customTheme)
  472.         self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin)
  473.         self.highlightTarget.trace_variable('w',self.VarChanged_highlightTarget)
  474.         self.keyBinding.trace_variable('w',self.VarChanged_keyBinding)
  475.         self.builtinKeys.trace_variable('w',self.VarChanged_builtinKeys)
  476.         self.customKeys.trace_variable('w',self.VarChanged_customKeys)
  477.         self.keysAreBuiltin.trace_variable('w',self.VarChanged_keysAreBuiltin)
  478.         self.winWidth.trace_variable('w',self.VarChanged_winWidth)
  479.         self.winHeight.trace_variable('w',self.VarChanged_winHeight)
  480.         self.paraWidth.trace_variable('w',self.VarChanged_paraWidth)
  481.         self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
  482.         self.autoSave.trace_variable('w',self.VarChanged_autoSave)
  483.         self.encoding.trace_variable('w',self.VarChanged_encoding)
  484.  
  485.     def VarChanged_fontSize(self,*params):
  486.         value=self.fontSize.get()
  487.         self.AddChangedItem('main','EditorWindow','font-size',value)
  488.  
  489.     def VarChanged_fontName(self,*params):
  490.         value=self.fontName.get()
  491.         self.AddChangedItem('main','EditorWindow','font',value)
  492.  
  493.     def VarChanged_fontBold(self,*params):
  494.         value=self.fontBold.get()
  495.         self.AddChangedItem('main','EditorWindow','font-bold',value)
  496.  
  497.     def VarChanged_indentBySpaces(self,*params):
  498.         value=self.indentBySpaces.get()
  499.         self.AddChangedItem('main','Indent','use-spaces',value)
  500.  
  501.     def VarChanged_spaceNum(self,*params):
  502.         value=self.spaceNum.get()
  503.         self.AddChangedItem('main','Indent','num-spaces',value)
  504.  
  505.     #def VarChanged_tabCols(self,*params):
  506.     #    value=self.tabCols.get()
  507.     #    self.AddChangedItem('main','Indent','tab-cols',value)
  508.  
  509.     def VarChanged_colour(self,*params):
  510.         self.OnNewColourSet()
  511.  
  512.     def VarChanged_builtinTheme(self,*params):
  513.         value=self.builtinTheme.get()
  514.         self.AddChangedItem('main','Theme','name',value)
  515.         self.PaintThemeSample()
  516.  
  517.     def VarChanged_customTheme(self,*params):
  518.         value=self.customTheme.get()
  519.         if value != '- no custom themes -':
  520.             self.AddChangedItem('main','Theme','name',value)
  521.             self.PaintThemeSample()
  522.  
  523.     def VarChanged_themeIsBuiltin(self,*params):
  524.         value=self.themeIsBuiltin.get()
  525.         self.AddChangedItem('main','Theme','default',value)
  526.         if value:
  527.             self.VarChanged_builtinTheme()
  528.         else:
  529.             self.VarChanged_customTheme()
  530.  
  531.     def VarChanged_highlightTarget(self,*params):
  532.         self.SetHighlightTarget()
  533.  
  534.     def VarChanged_keyBinding(self,*params):
  535.         value=self.keyBinding.get()
  536.         keySet=self.customKeys.get()
  537.         event=self.listBindings.get(ANCHOR).split()[0]
  538.         if idleConf.IsCoreBinding(event):
  539.             #this is a core keybinding
  540.             self.AddChangedItem('keys',keySet,event,value)
  541.         else: #this is an extension key binding
  542.             extName=idleConf.GetExtnNameForEvent(event)
  543.             extKeybindSection=extName+'_cfgBindings'
  544.             self.AddChangedItem('extensions',extKeybindSection,event,value)
  545.  
  546.     def VarChanged_builtinKeys(self,*params):
  547.         value=self.builtinKeys.get()
  548.         self.AddChangedItem('main','Keys','name',value)
  549.         self.LoadKeysList(value)
  550.  
  551.     def VarChanged_customKeys(self,*params):
  552.         value=self.customKeys.get()
  553.         if value != '- no custom keys -':
  554.             self.AddChangedItem('main','Keys','name',value)
  555.             self.LoadKeysList(value)
  556.  
  557.     def VarChanged_keysAreBuiltin(self,*params):
  558.         value=self.keysAreBuiltin.get()
  559.         self.AddChangedItem('main','Keys','default',value)
  560.         if value:
  561.             self.VarChanged_builtinKeys()
  562.         else:
  563.             self.VarChanged_customKeys()
  564.  
  565.     def VarChanged_winWidth(self,*params):
  566.         value=self.winWidth.get()
  567.         self.AddChangedItem('main','EditorWindow','width',value)
  568.  
  569.     def VarChanged_winHeight(self,*params):
  570.         value=self.winHeight.get()
  571.         self.AddChangedItem('main','EditorWindow','height',value)
  572.  
  573.     def VarChanged_paraWidth(self,*params):
  574.         value=self.paraWidth.get()
  575.         self.AddChangedItem('main','FormatParagraph','paragraph',value)
  576.  
  577.     def VarChanged_startupEdit(self,*params):
  578.         value=self.startupEdit.get()
  579.         self.AddChangedItem('main','General','editor-on-startup',value)
  580.  
  581.     def VarChanged_autoSave(self,*params):
  582.         value=self.autoSave.get()
  583.         self.AddChangedItem('main','General','autosave',value)
  584.  
  585.     def VarChanged_encoding(self,*params):
  586.         value=self.encoding.get()
  587.         self.AddChangedItem('main','EditorWindow','encoding',value)
  588.  
  589.     def ResetChangedItems(self):
  590.         #When any config item is changed in this dialog, an entry
  591.         #should be made in the relevant section (config type) of this
  592.         #dictionary. The key should be the config file section name and the
  593.         #value a dictionary, whose key:value pairs are item=value pairs for
  594.         #that config file section.
  595.         self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
  596.  
  597.     def AddChangedItem(self,type,section,item,value):
  598.         value=str(value) #make sure we use a string
  599.         if not self.changedItems[type].has_key(section):
  600.             self.changedItems[type][section]={}
  601.         self.changedItems[type][section][item]=value
  602.  
  603.     def GetDefaultItems(self):
  604.         dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
  605.         for configType in dItems.keys():
  606.             sections=idleConf.GetSectionList('default',configType)
  607.             for section in sections:
  608.                 dItems[configType][section]={}
  609.                 options=idleConf.defaultCfg[configType].GetOptionList(section)
  610.                 for option in options:
  611.                     dItems[configType][section][option]=(
  612.                             idleConf.defaultCfg[configType].Get(section,option))
  613.         return dItems
  614.  
  615.     def SetThemeType(self):
  616.         if self.themeIsBuiltin.get():
  617.             self.optMenuThemeBuiltin.config(state=NORMAL)
  618.             self.optMenuThemeCustom.config(state=DISABLED)
  619.             self.buttonDeleteCustomTheme.config(state=DISABLED)
  620.         else:
  621.             self.optMenuThemeBuiltin.config(state=DISABLED)
  622.             self.radioThemeCustom.config(state=NORMAL)
  623.             self.optMenuThemeCustom.config(state=NORMAL)
  624.             self.buttonDeleteCustomTheme.config(state=NORMAL)
  625.  
  626.     def SetKeysType(self):
  627.         if self.keysAreBuiltin.get():
  628.             self.optMenuKeysBuiltin.config(state=NORMAL)
  629.             self.optMenuKeysCustom.config(state=DISABLED)
  630.             self.buttonDeleteCustomKeys.config(state=DISABLED)
  631.         else:
  632.             self.optMenuKeysBuiltin.config(state=DISABLED)
  633.             self.radioKeysCustom.config(state=NORMAL)
  634.             self.optMenuKeysCustom.config(state=NORMAL)
  635.             self.buttonDeleteCustomKeys.config(state=NORMAL)
  636.  
  637.     def GetNewKeys(self):
  638.         listIndex=self.listBindings.index(ANCHOR)
  639.         binding=self.listBindings.get(listIndex)
  640.         bindName=binding.split()[0] #first part, up to first space
  641.         if self.keysAreBuiltin.get():
  642.             currentKeySetName=self.builtinKeys.get()
  643.         else:
  644.             currentKeySetName=self.customKeys.get()
  645.         currentBindings=idleConf.GetCurrentKeySet()
  646.         if currentKeySetName in self.changedItems['keys'].keys(): #unsaved changes
  647.             keySetChanges=self.changedItems['keys'][currentKeySetName]
  648.             for event in keySetChanges.keys():
  649.                 currentBindings[event]=keySetChanges[event].split()
  650.         currentKeySequences=currentBindings.values()
  651.         newKeys=GetKeysDialog(self,'Get New Keys',bindName,
  652.                 currentKeySequences).result
  653.         if newKeys: #new keys were specified
  654.             if self.keysAreBuiltin.get(): #current key set is a built-in
  655.                 message=('Your changes will be saved as a new Custom Key Set. '+
  656.                         'Enter a name for your new Custom Key Set below.')
  657.                 newKeySet=self.GetNewKeysName(message)
  658.                 if not newKeySet: #user cancelled custom key set creation
  659.                     self.listBindings.select_set(listIndex)
  660.                     self.listBindings.select_anchor(listIndex)
  661.                     return
  662.                 else: #create new custom key set based on previously active key set
  663.                     self.CreateNewKeySet(newKeySet)
  664.             self.listBindings.delete(listIndex)
  665.             self.listBindings.insert(listIndex,bindName+' - '+newKeys)
  666.             self.listBindings.select_set(listIndex)
  667.             self.listBindings.select_anchor(listIndex)
  668.             self.keyBinding.set(newKeys)
  669.         else:
  670.             self.listBindings.select_set(listIndex)
  671.             self.listBindings.select_anchor(listIndex)
  672.  
  673.     def GetNewKeysName(self,message):
  674.         usedNames=(idleConf.GetSectionList('user','keys')+
  675.                 idleConf.GetSectionList('default','keys'))
  676.         newKeySet=GetCfgSectionNameDialog(self,'New Custom Key Set',
  677.                 message,usedNames).result
  678.         return newKeySet
  679.  
  680.     def SaveAsNewKeySet(self):
  681.         newKeysName=self.GetNewKeysName('New Key Set Name:')
  682.         if newKeysName:
  683.             self.CreateNewKeySet(newKeysName)
  684.  
  685.     def KeyBindingSelected(self,event):
  686.         self.buttonNewKeys.config(state=NORMAL)
  687.  
  688.     def CreateNewKeySet(self,newKeySetName):
  689.         #creates new custom key set based on the previously active key set,
  690.         #and makes the new key set active
  691.         if self.keysAreBuiltin.get():
  692.             prevKeySetName=self.builtinKeys.get()
  693.         else:
  694.             prevKeySetName=self.customKeys.get()
  695.         prevKeys=idleConf.GetCoreKeys(prevKeySetName)
  696.         newKeys={}
  697.         for event in prevKeys.keys(): #add key set to changed items
  698.             eventName=event[2:-2] #trim off the angle brackets
  699.             binding=string.join(prevKeys[event])
  700.             newKeys[eventName]=binding
  701.         #handle any unsaved changes to prev key set
  702.         if prevKeySetName in self.changedItems['keys'].keys():
  703.             keySetChanges=self.changedItems['keys'][prevKeySetName]
  704.             for event in keySetChanges.keys():
  705.                 newKeys[event]=keySetChanges[event]
  706.         #save the new theme
  707.         self.SaveNewKeySet(newKeySetName,newKeys)
  708.         #change gui over to the new key set
  709.         customKeyList=idleConf.GetSectionList('user','keys')
  710.         customKeyList.sort()
  711.         self.optMenuKeysCustom.SetMenu(customKeyList,newKeySetName)
  712.         self.keysAreBuiltin.set(0)
  713.         self.SetKeysType()
  714.  
  715.     def LoadKeysList(self,keySetName):
  716.         reselect=0
  717.         newKeySet=0
  718.         if self.listBindings.curselection():
  719.             reselect=1
  720.             listIndex=self.listBindings.index(ANCHOR)
  721.         keySet=idleConf.GetKeySet(keySetName)
  722.         bindNames=keySet.keys()
  723.         bindNames.sort()
  724.         self.listBindings.delete(0,END)
  725.         for bindName in bindNames:
  726.             key=string.join(keySet[bindName]) #make key(s) into a string
  727.             bindName=bindName[2:-2] #trim off the angle brackets
  728.             if keySetName in self.changedItems['keys'].keys():
  729.                 #handle any unsaved changes to this key set
  730.                 if bindName in self.changedItems['keys'][keySetName].keys():
  731.                     key=self.changedItems['keys'][keySetName][bindName]
  732.             self.listBindings.insert(END, bindName+' - '+key)
  733.         if reselect:
  734.             self.listBindings.see(listIndex)
  735.             self.listBindings.select_set(listIndex)
  736.             self.listBindings.select_anchor(listIndex)
  737.  
  738.     def DeleteCustomKeys(self):
  739.         keySetName=self.customKeys.get()
  740.         if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
  741.                                      'to delete the key set %r ?' % (keySetName),
  742.                                      parent=self):
  743.             return
  744.         #remove key set from config
  745.         idleConf.userCfg['keys'].remove_section(keySetName)
  746.         if self.changedItems['keys'].has_key(keySetName):
  747.             del(self.changedItems['keys'][keySetName])
  748.         #write changes
  749.         idleConf.userCfg['keys'].Save()
  750.         #reload user key set list
  751.         itemList=idleConf.GetSectionList('user','keys')
  752.         itemList.sort()
  753.         if not itemList:
  754.             self.radioKeysCustom.config(state=DISABLED)
  755.             self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
  756.         else:
  757.             self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
  758.         #revert to default key set
  759.         self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
  760.         self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
  761.         #user can't back out of these changes, they must be applied now
  762.         self.Apply()
  763.         self.SetKeysType()
  764.  
  765.     def DeleteCustomTheme(self):
  766.         themeName=self.customTheme.get()
  767.         if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
  768.                                      'to delete the theme %r ?' % (themeName,),
  769.                                      parent=self):
  770.             return
  771.         #remove theme from config
  772.         idleConf.userCfg['highlight'].remove_section(themeName)
  773.         if self.changedItems['highlight'].has_key(themeName):
  774.             del(self.changedItems['highlight'][themeName])
  775.         #write changes
  776.         idleConf.userCfg['highlight'].Save()
  777.         #reload user theme list
  778.         itemList=idleConf.GetSectionList('user','highlight')
  779.         itemList.sort()
  780.         if not itemList:
  781.             self.radioThemeCustom.config(state=DISABLED)
  782.             self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
  783.         else:
  784.             self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
  785.         #revert to default theme
  786.         self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
  787.         self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
  788.         #user can't back out of these changes, they must be applied now
  789.         self.Apply()
  790.         self.SetThemeType()
  791.  
  792.     def GetColour(self):
  793.         target=self.highlightTarget.get()
  794.         prevColour=self.frameColourSet.cget('bg')
  795.         rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
  796.             title='Pick new colour for : '+target,initialcolor=prevColour)
  797.         if colourString and (colourString!=prevColour):
  798.             #user didn't cancel, and they chose a new colour
  799.             if self.themeIsBuiltin.get(): #current theme is a built-in
  800.                 message=('Your changes will be saved as a new Custom Theme. '+
  801.                         'Enter a name for your new Custom Theme below.')
  802.                 newTheme=self.GetNewThemeName(message)
  803.                 if not newTheme: #user cancelled custom theme creation
  804.                     return
  805.                 else: #create new custom theme based on previously active theme
  806.                     self.CreateNewTheme(newTheme)
  807.                     self.colour.set(colourString)
  808.             else: #current theme is user defined
  809.                 self.colour.set(colourString)
  810.  
  811.     def OnNewColourSet(self):
  812.         newColour=self.colour.get()
  813.         self.frameColourSet.config(bg=newColour)#set sample
  814.         if self.fgHilite.get(): plane='foreground'
  815.         else: plane='background'
  816.         sampleElement=self.themeElements[self.highlightTarget.get()][0]
  817.         self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
  818.         theme=self.customTheme.get()
  819.         themeElement=sampleElement+'-'+plane
  820.         self.AddChangedItem('highlight',theme,themeElement,newColour)
  821.  
  822.     def GetNewThemeName(self,message):
  823.         usedNames=(idleConf.GetSectionList('user','highlight')+
  824.                 idleConf.GetSectionList('default','highlight'))
  825.         newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
  826.                 message,usedNames).result
  827.         return newTheme
  828.  
  829.     def SaveAsNewTheme(self):
  830.         newThemeName=self.GetNewThemeName('New Theme Name:')
  831.         if newThemeName:
  832.             self.CreateNewTheme(newThemeName)
  833.  
  834.     def CreateNewTheme(self,newThemeName):
  835.         #creates new custom theme based on the previously active theme,
  836.         #and makes the new theme active
  837.         if self.themeIsBuiltin.get():
  838.             themeType='default'
  839.             themeName=self.builtinTheme.get()
  840.         else:
  841.             themeType='user'
  842.             themeName=self.customTheme.get()
  843.         newTheme=idleConf.GetThemeDict(themeType,themeName)
  844.         #apply any of the old theme's unsaved changes to the new theme
  845.         if themeName in self.changedItems['highlight'].keys():
  846.             themeChanges=self.changedItems['highlight'][themeName]
  847.             for element in themeChanges.keys():
  848.                 newTheme[element]=themeChanges[element]
  849.         #save the new theme
  850.         self.SaveNewTheme(newThemeName,newTheme)
  851.         #change gui over to the new theme
  852.         customThemeList=idleConf.GetSectionList('user','highlight')
  853.         customThemeList.sort()
  854.         self.optMenuThemeCustom.SetMenu(customThemeList,newThemeName)
  855.         self.themeIsBuiltin.set(0)
  856.         self.SetThemeType()
  857.  
  858.     def OnListFontButtonRelease(self,event):
  859.         font = self.listFontName.get(ANCHOR)
  860.         self.fontName.set(font.lower())
  861.         self.SetFontSample()
  862.  
  863.     def SetFontSample(self,event=None):
  864.         fontName=self.fontName.get()
  865.         if self.fontBold.get():
  866.             fontWeight=tkFont.BOLD
  867.         else:
  868.             fontWeight=tkFont.NORMAL
  869.         self.editFont.config(size=self.fontSize.get(),
  870.                 weight=fontWeight,family=fontName)
  871.  
  872.     def SetHighlightTarget(self):
  873.         if self.highlightTarget.get()=='Cursor': #bg not possible
  874.             self.radioFg.config(state=DISABLED)
  875.             self.radioBg.config(state=DISABLED)
  876.             self.fgHilite.set(1)
  877.         else: #both fg and bg can be set
  878.             self.radioFg.config(state=NORMAL)
  879.             self.radioBg.config(state=NORMAL)
  880.             self.fgHilite.set(1)
  881.         self.SetColourSample()
  882.  
  883.     def SetColourSampleBinding(self,*args):
  884.         self.SetColourSample()
  885.  
  886.     def SetColourSample(self):
  887.         #set the colour smaple area
  888.         tag=self.themeElements[self.highlightTarget.get()][0]
  889.         if self.fgHilite.get(): plane='foreground'
  890.         else: plane='background'
  891.         colour=self.textHighlightSample.tag_cget(tag,plane)
  892.         self.frameColourSet.config(bg=colour)
  893.  
  894.     def PaintThemeSample(self):
  895.         if self.themeIsBuiltin.get(): #a default theme
  896.             theme=self.builtinTheme.get()
  897.         else: #a user theme
  898.             theme=self.customTheme.get()
  899.         for elementTitle in self.themeElements.keys():
  900.             element=self.themeElements[elementTitle][0]
  901.             colours=idleConf.GetHighlight(theme,element)
  902.             if element=='cursor': #cursor sample needs special painting
  903.                 colours['background']=idleConf.GetHighlight(theme,
  904.                         'normal', fgBg='bg')
  905.             #handle any unsaved changes to this theme
  906.             if theme in self.changedItems['highlight'].keys():
  907.                 themeDict=self.changedItems['highlight'][theme]
  908.                 if themeDict.has_key(element+'-foreground'):
  909.                     colours['foreground']=themeDict[element+'-foreground']
  910.                 if themeDict.has_key(element+'-background'):
  911.                     colours['background']=themeDict[element+'-background']
  912.             self.textHighlightSample.tag_config(element, **colours)
  913.         self.SetColourSample()
  914.  
  915. ##     def OnCheckUserHelpBrowser(self):
  916. ##         if self.userHelpBrowser.get():
  917. ##             self.entryHelpBrowser.config(state=NORMAL)
  918. ##         else:
  919. ##             self.entryHelpBrowser.config(state=DISABLED)
  920.  
  921.     def HelpSourceSelected(self,event):
  922.         self.SetHelpListButtonStates()
  923.  
  924.     def SetHelpListButtonStates(self):
  925.         if self.listHelp.size()<1: #no entries in list
  926.             self.buttonHelpListEdit.config(state=DISABLED)
  927.             self.buttonHelpListRemove.config(state=DISABLED)
  928.         else: #there are some entries
  929.             if self.listHelp.curselection(): #there currently is a selection
  930.                 self.buttonHelpListEdit.config(state=NORMAL)
  931.                 self.buttonHelpListRemove.config(state=NORMAL)
  932.             else:  #there currently is not a selection
  933.                 self.buttonHelpListEdit.config(state=DISABLED)
  934.                 self.buttonHelpListRemove.config(state=DISABLED)
  935.  
  936.     def HelpListItemAdd(self):
  937.         helpSource=GetHelpSourceDialog(self,'New Help Source').result
  938.         if helpSource:
  939.             self.userHelpList.append( (helpSource[0],helpSource[1]) )
  940.             self.listHelp.insert(END,helpSource[0])
  941.             self.UpdateUserHelpChangedItems()
  942.         self.SetHelpListButtonStates()
  943.  
  944.     def HelpListItemEdit(self):
  945.         itemIndex=self.listHelp.index(ANCHOR)
  946.         helpSource=self.userHelpList[itemIndex]
  947.         newHelpSource=GetHelpSourceDialog(self,'Edit Help Source',
  948.                 menuItem=helpSource[0],filePath=helpSource[1]).result
  949.         if (not newHelpSource) or (newHelpSource==helpSource):
  950.             return #no changes
  951.         self.userHelpList[itemIndex]=newHelpSource
  952.         self.listHelp.delete(itemIndex)
  953.         self.listHelp.insert(itemIndex,newHelpSource[0])
  954.         self.UpdateUserHelpChangedItems()
  955.         self.SetHelpListButtonStates()
  956.  
  957.     def HelpListItemRemove(self):
  958.         itemIndex=self.listHelp.index(ANCHOR)
  959.         del(self.userHelpList[itemIndex])
  960.         self.listHelp.delete(itemIndex)
  961.         self.UpdateUserHelpChangedItems()
  962.         self.SetHelpListButtonStates()
  963.  
  964.     def UpdateUserHelpChangedItems(self):
  965.         "Clear and rebuild the HelpFiles section in self.changedItems"
  966.         self.changedItems['main']['HelpFiles'] = {}
  967.         for num in range(1,len(self.userHelpList)+1):
  968.             self.AddChangedItem('main','HelpFiles',str(num),
  969.                     string.join(self.userHelpList[num-1][:2],';'))
  970.  
  971.     def LoadFontCfg(self):
  972.         ##base editor font selection list
  973.         fonts=list(tkFont.families(self))
  974.         fonts.sort()
  975.         for font in fonts:
  976.             self.listFontName.insert(END,font)
  977.         configuredFont=idleConf.GetOption('main','EditorWindow','font',
  978.                 default='courier')
  979.         lc_configuredFont = configuredFont.lower()
  980.         self.fontName.set(lc_configuredFont)
  981.         lc_fonts = [s.lower() for s in fonts]
  982.         if lc_configuredFont in lc_fonts:
  983.             currentFontIndex = lc_fonts.index(lc_configuredFont)
  984.             self.listFontName.see(currentFontIndex)
  985.             self.listFontName.select_set(currentFontIndex)
  986.             self.listFontName.select_anchor(currentFontIndex)
  987.         ##font size dropdown
  988.         fontSize=idleConf.GetOption('main','EditorWindow','font-size',
  989.                 default='10')
  990.         self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
  991.                 '16','18','20','22'),fontSize )
  992.         ##fontWeight
  993.         self.fontBold.set(idleConf.GetOption('main','EditorWindow',
  994.                 'font-bold',default=0,type='bool'))
  995.         ##font sample
  996.         self.SetFontSample()
  997.  
  998.     def LoadTabCfg(self):
  999.         ##indent type radiobuttons
  1000.         spaceIndent=idleConf.GetOption('main','Indent','use-spaces',
  1001.                 default=1,type='bool')
  1002.         self.indentBySpaces.set(spaceIndent)
  1003.         ##indent sizes
  1004.         spaceNum=idleConf.GetOption('main','Indent','num-spaces',
  1005.                 default=4,type='int')
  1006.         #tabCols=idleConf.GetOption('main','Indent','tab-cols',
  1007.         #        default=4,type='int')
  1008.         self.spaceNum.set(spaceNum)
  1009.         #self.tabCols.set(tabCols)
  1010.  
  1011.     def LoadThemeCfg(self):
  1012.         ##current theme type radiobutton
  1013.         self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
  1014.             type='bool',default=1))
  1015.         ##currently set theme
  1016.         currentOption=idleConf.CurrentTheme()
  1017.         ##load available theme option menus
  1018.         if self.themeIsBuiltin.get(): #default theme selected
  1019.             itemList=idleConf.GetSectionList('default','highlight')
  1020.             itemList.sort()
  1021.             self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
  1022.             itemList=idleConf.GetSectionList('user','highlight')
  1023.             itemList.sort()
  1024.             if not itemList:
  1025.                 self.radioThemeCustom.config(state=DISABLED)
  1026.                 self.customTheme.set('- no custom themes -')
  1027.             else:
  1028.                 self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
  1029.         else: #user theme selected
  1030.             itemList=idleConf.GetSectionList('user','highlight')
  1031.             itemList.sort()
  1032.             self.optMenuThemeCustom.SetMenu(itemList,currentOption)
  1033.             itemList=idleConf.GetSectionList('default','highlight')
  1034.             itemList.sort()
  1035.             self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
  1036.         self.SetThemeType()
  1037.         ##load theme element option menu
  1038.         themeNames=self.themeElements.keys()
  1039.         themeNames.sort(self.__ThemeNameIndexCompare)
  1040.         self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])
  1041.         self.PaintThemeSample()
  1042.         self.SetHighlightTarget()
  1043.  
  1044.     def __ThemeNameIndexCompare(self,a,b):
  1045.         if self.themeElements[a][1]<self.themeElements[b][1]: return -1
  1046.         elif self.themeElements[a][1]==self.themeElements[b][1]: return 0
  1047.         else: return 1
  1048.  
  1049.     def LoadKeyCfg(self):
  1050.         ##current keys type radiobutton
  1051.         self.keysAreBuiltin.set(idleConf.GetOption('main','Keys','default',
  1052.             type='bool',default=1))
  1053.         ##currently set keys
  1054.         currentOption=idleConf.CurrentKeys()
  1055.         ##load available keyset option menus
  1056.         if self.keysAreBuiltin.get(): #default theme selected
  1057.             itemList=idleConf.GetSectionList('default','keys')
  1058.             itemList.sort()
  1059.             self.optMenuKeysBuiltin.SetMenu(itemList,currentOption)
  1060.             itemList=idleConf.GetSectionList('user','keys')
  1061.             itemList.sort()
  1062.             if not itemList:
  1063.                 self.radioKeysCustom.config(state=DISABLED)
  1064.                 self.customKeys.set('- no custom keys -')
  1065.             else:
  1066.                 self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
  1067.         else: #user key set selected
  1068.             itemList=idleConf.GetSectionList('user','keys')
  1069.             itemList.sort()
  1070.             self.optMenuKeysCustom.SetMenu(itemList,currentOption)
  1071.             itemList=idleConf.GetSectionList('default','keys')
  1072.             itemList.sort()
  1073.             self.optMenuKeysBuiltin.SetMenu(itemList,itemList[0])
  1074.         self.SetKeysType()
  1075.         ##load keyset element list
  1076.         keySetName=idleConf.CurrentKeys()
  1077.         self.LoadKeysList(keySetName)
  1078.  
  1079.     def LoadGeneralCfg(self):
  1080.         #startup state
  1081.         self.startupEdit.set(idleConf.GetOption('main','General',
  1082.                 'editor-on-startup',default=1,type='bool'))
  1083.         #autosave state
  1084.         self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave',
  1085.                                              default=0, type='bool'))
  1086.         #initial window size
  1087.         self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))
  1088.         self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
  1089.         #initial paragraph reformat size
  1090.         self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph'))
  1091.         # default source encoding
  1092.         self.encoding.set(idleConf.GetOption('main', 'EditorWindow',
  1093.                                              'encoding', default='none'))
  1094.         # additional help sources
  1095.         self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
  1096.         for helpItem in self.userHelpList:
  1097.             self.listHelp.insert(END,helpItem[0])
  1098.         self.SetHelpListButtonStates()
  1099.         #self.userHelpBrowser.set(idleConf.GetOption('main','General',
  1100.         #        'user-help-browser',default=0,type='bool'))
  1101.         #self.helpBrowser.set(idleConf.GetOption('main','General',
  1102.         #        'user-help-browser-command',default=''))
  1103.         #self.OnCheckUserHelpBrowser()
  1104.  
  1105.     def LoadConfigs(self):
  1106.         """
  1107.         load configuration from default and user config files and populate
  1108.         the widgets on the config dialog pages.
  1109.         """
  1110.         ### fonts / tabs page
  1111.         self.LoadFontCfg()
  1112.         self.LoadTabCfg()
  1113.         ### highlighting page
  1114.         self.LoadThemeCfg()
  1115.         ### keys page
  1116.         self.LoadKeyCfg()
  1117.         ### general page
  1118.         self.LoadGeneralCfg()
  1119.  
  1120.     def SaveNewKeySet(self,keySetName,keySet):
  1121.         """
  1122.         save a newly created core key set.
  1123.         keySetName - string, the name of the new key set
  1124.         keySet - dictionary containing the new key set
  1125.         """
  1126.         if not idleConf.userCfg['keys'].has_section(keySetName):
  1127.             idleConf.userCfg['keys'].add_section(keySetName)
  1128.         for event in keySet.keys():
  1129.             value=keySet[event]
  1130.             idleConf.userCfg['keys'].SetOption(keySetName,event,value)
  1131.  
  1132.     def SaveNewTheme(self,themeName,theme):
  1133.         """
  1134.         save a newly created theme.
  1135.         themeName - string, the name of the new theme
  1136.         theme - dictionary containing the new theme
  1137.         """
  1138.         if not idleConf.userCfg['highlight'].has_section(themeName):
  1139.             idleConf.userCfg['highlight'].add_section(themeName)
  1140.         for element in theme.keys():
  1141.             value=theme[element]
  1142.             idleConf.userCfg['highlight'].SetOption(themeName,element,value)
  1143.  
  1144.     def SetUserValue(self,configType,section,item,value):
  1145.         if idleConf.defaultCfg[configType].has_option(section,item):
  1146.             if idleConf.defaultCfg[configType].Get(section,item)==value:
  1147.                 #the setting equals a default setting, remove it from user cfg
  1148.                 return idleConf.userCfg[configType].RemoveOption(section,item)
  1149.         #if we got here set the option
  1150.         return idleConf.userCfg[configType].SetOption(section,item,value)
  1151.  
  1152.     def SaveAllChangedConfigs(self):
  1153.         "Save configuration changes to the user config file."
  1154.         idleConf.userCfg['main'].Save()
  1155.         for configType in self.changedItems.keys():
  1156.             cfgTypeHasChanges = False
  1157.             for section in self.changedItems[configType].keys():
  1158.                 if section == 'HelpFiles':
  1159.                     #this section gets completely replaced
  1160.                     idleConf.userCfg['main'].remove_section('HelpFiles')
  1161.                     cfgTypeHasChanges = True
  1162.                 for item in self.changedItems[configType][section].keys():
  1163.                     value = self.changedItems[configType][section][item]
  1164.                     if self.SetUserValue(configType,section,item,value):
  1165.                         cfgTypeHasChanges = True
  1166.             if cfgTypeHasChanges:
  1167.                 idleConf.userCfg[configType].Save()
  1168.         for configType in ['keys', 'highlight']:
  1169.             # save these even if unchanged!
  1170.             idleConf.userCfg[configType].Save()
  1171.         self.ResetChangedItems() #clear the changed items dict
  1172.  
  1173.     def ActivateConfigChanges(self):
  1174.         #things that need to be done to make
  1175.         #applied config changes dynamic:
  1176.         #update editor/shell font and repaint
  1177.         #dynamically update indentation setttings
  1178.         #update theme and repaint
  1179.         #update keybindings and re-bind
  1180.         #update user help sources menu
  1181.         winInstances=self.parent.instance_dict.keys()
  1182.         for instance in winInstances:
  1183.             instance.ResetColorizer()
  1184.             instance.ResetFont()
  1185.             instance.ResetKeybindings()
  1186.             instance.reset_help_menu_entries()
  1187.  
  1188.     def Cancel(self):
  1189.         self.destroy()
  1190.  
  1191.     def Ok(self):
  1192.         self.Apply()
  1193.         self.destroy()
  1194.  
  1195.     def Apply(self):
  1196.         self.SaveAllChangedConfigs()
  1197.         self.ActivateConfigChanges()
  1198.  
  1199.     def Help(self):
  1200.         pass
  1201.  
  1202. if __name__ == '__main__':
  1203.     #test the dialog
  1204.     root=Tk()
  1205.     Button(root,text='Dialog',
  1206.             command=lambda:ConfigDialog(root,'Settings')).pack()
  1207.     root.instance_dict={}
  1208.     root.mainloop()
  1209.